Colibri Hiragana Quiz - Felix John COLIBRI. |
- abstract : a project to accelerate the learning of the Japanese Hiragana alphabet. The user selects a subrange of the 46 characters, a random question is presented and the answer has to be selected among 3* 3 possible
solutions. 3 versions: romaji to hiragana, hiragana to romaji, sound to hiragana. Success statistics are computed for each character.
- key words : Japanese quiz, Japanese Hiragana tutorial, Delphi 6 unicode, playing .wav, playing .mp3
- software used : Windows XP Home, Delphi 6
- hardware used : Pentium 2.800Mhz, 512 M memory, 140 G hard disc
- scope : Delphi 5, 6, 7, 8 Delphi 2005 to 2010, Rad Studio Xe to Xe8, Delpi Seattle, Delphi Berlin
- level : Delphi developer, Japanese beginner
- plan :
1 - Learning Japanese Hiragana
My wife and me plan to visit Japan. To be able to talk a little bit with the Japanese people, I decided to start learning Japanese. This article presents a small utility we wrote to accelerate this training.
1.1 - English, Romaji, Hiragana, Katakana, Kanji As you might know, the Japanese language uses 3 alphabets : - Hiragana, which has around 50 characters
- Katakana, mostly used for writing foreign origin words (like TV)
- Kanji which is derived from the Chinese alphabet.
In the tutorials, the pronounciation of the words is often written in "romaji",
wich uses our 26 letter "roman" alphabet. Reading a romaji text in english somehow corresponds to Japanese pronounciation. This is not totally correct but reasonable for a first approach.
Most tutorials on the Web or learning books start with some words or simple sentences. Those are first presented in english with a romaji translation. And the Hiragana writing of the romaji is then gradually introduced. And later, the
Kanji version, if any, are taught. For "thank you", the different writings are: In addition - the textbooks also indicate that in the case of "thank you", the Kanji writing is unusual
- you will notice that the Kanji words contains a last Hiragana character
In the any newspaper story, you will find Hiragana, Katakana and Kanji mixed. Here is a copy of some "Yomuri Online" page we randomly picked from their site:
I have no idea (yet !) of what happened to this unfortunate feller, but it is easy to see that the 3 alphabets are used throughout the text.
Traditionally, Japanese youngsters first learn Hiragana, then gradually learn Kanji. Unsurprisingly, we decided to follow the same route and try to tackle the Hiragana first. And this is the reason of this project.
2 - The Hiragana alphabet 2.1 - The Hiragana alphabet The Hiragana characters use the U+3040..U+309F bloc (96 code points) of Unicode. So this is the $3040 .. $309F bloc of the Unicode Bmp (Basic
MultiLingual Plane) :
To display a single Hiragana character, we use the Canvas.TextOutW method.
This method uses a pWideChar. This pointer is used to cast a WideChar character that we initialized using a WideChar cast of the code unit
Var l_wide_string: WideString; l_wide_string:= ' ';
l_wide_string[1]:= WideChar(p_code_unit);
TextOutW(Canvas.Handle, p_x, p_y, pWideChar(l_wide_string), 1);
| This fancy legwork could have been avoided with any "post Delphi 2010 version", like Delphi Xe8, but at the cost of a hugely larger .EXE. Anyway, here is the code:
Procedure c_hiragana_paintbox.display_hiragana_xy(p_x, p_y, p_code_unit: Integer);
Var l_wide_string: WideString; Begin
If p_code_unit= k_error_code_unit
Then draw_error_code_unit(p_x, p_y)
Else Begin
l_wide_string:= ' ';
l_wide_string[1]:= WideChar(p_code_unit);
TextOutW(Canvas.Handle, p_x, p_y, pWideChar(l_wide_string), 1);
End; End; // display_hiragana_xy
Procedure c_all_hiragana_paintbox.handle_paint(p_c_paintbox: tObject);
Procedure _display_column_title;
Var l_column: Integer;
l_column_number: String; Begin
For l_column:= 0 To 15 Do
Begin
l_column_number:= IntToHex(l_column, 1);
display_hiragana_xy(f_x(l_column), k_fat, Ord(l_column_number[1]));
End; End; // _display_column_title
Procedure _display_hiragana;
Var l_row, l_column: Integer;
l_character_code_unit: Integer; Begin
l_character_code_unit:= $3040;
For l_row:= 0 To m_row_count- 1 Do
For l_column:= 0 To m_column_count- 1 Do
Begin
display_hiragana_xy(f_x(l_column), f_y(1+ l_row), l_character_code_unit);
Inc(l_character_code_unit); End;
End; // _display_hiragana Begin // handle_paint
Inherited handle_paint(p_c_paintbox); _display_column_title;
_display_hiragana; End; // handle_paint |
And here is the result:
Basically, this tables contains - the 5 vowels, a i u e o (yes they are not in the traditional latin "a e i o u" order)
- syllables built using a consonant and the same vowels, like ka, ki, ky etc
This is represented by the classical table: And
- each row contains one vowel, for instance a, ka, sa, ta etc
- each column contains the syllables built using one consonant.
- for the first column, we have the vowels a i u e o
- For the "K" column, we have ka, ki, ku, ke, ko
This table is sometimes presented vertically or with some other variations. We will stick to this layout.
As you can see, the hiragana code bloc contains all those characters, but not in the same order. And characters have 0, 1 or 2 accents, using 1, 2 or 3 code units :
2.2 - The canonical column presentation So there is no simple way to transform the code bloc into the traditional column presentation. We therefore used a simple mapping scheme which is coded like this:
Procedure c_column_hiragana_paintbox.initialize_paintbox(
p_font_size, p_x_0, p_y_0, p_row_count, p_column_count: Integer);
Procedure _initialize_code_units;
Var l_c_check_unique_name_list: tStringList;
Procedure _initialize(p_row, p_column, p_code_unit: Integer;
p_name: String); Begin
With m_hiragana_array[p_row, p_column] Do
Begin m_code_unit:= p_code_unit;
If l_c_check_unique_name_list.IndexOf(p_name)>= 0
Then display_bug_stop('twice name '+ p_name);
m_name:= p_name;
If m_name[1]= m_name[2]
Then m_mnemonic:= m_name[1]
Else m_mnemonic:= m_name;
m_row:= p_row;
m_column:= p_column; End;
End; // _initialize
Const k_vowel: Array[0..4] Of Char= ('a', 'i', 'u', 'e', 'o');
Var l_row: Integer;
Begin // _initialize_code_units
l_c_check_unique_name_list:= f_c_create_sorted_no_duplicate_caseinsensitive_stringlist;
// -- a _initialize(0, 0, $3042, 'aa');
_initialize(1, 0, $3044, 'ii');
_initialize(2, 0, $3046, 'uu');
_initialize(3, 0, $3048, 'ee');
_initialize(4, 0, $304A, 'oo');
// -- ka
_initialize(0, 1, $304B, 'ka');
_initialize(1, 1, $304D, 'ki');
_initialize(2, 1, $304F, 'ku');
_initialize(3, 1, $3051, 'ke');
_initialize(4, 1, $3053, 'ko'); // -- sa
For l_row:= 0 To 4 Do
_initialize(l_row, 2, $3055+ l_row* 2, 's'+ k_vowel[l_row]);
// -- ta
_initialize(0, 3, $305F, 'ta');
_initialize(1, 3, $3061, 'ti');
_initialize(2, 3, $3064, 'tu');
_initialize(3, 3, $3066, 'te');
_initialize(4, 3, $3068, 'to'); // -- na
_initialize(0, 4, $306A, 'na');
_initialize(1, 4, $306B, 'ni');
_initialize(2, 4, $306C, 'nu');
_initialize(3, 4, $306D, 'ne');
_initialize(4, 4, $306E, 'no');
// -- ha
_initialize(0, 5, $306F, 'ha');
_initialize(1, 5, $3072, 'hi');
_initialize(2, 5, $3075, 'hu');
_initialize(3, 5, $3078, 'he');
_initialize(4, 5, $307B, 'ho');
// -- ma
_initialize(0, 6, $307E, 'ma');
_initialize(1, 6, $307F, 'mi');
_initialize(2, 6, $3080, 'mu');
_initialize(3, 6, $3081, 'me');
_initialize(4, 6, $3082, 'mo'); // -- ya
_initialize(0, 7, $3084, 'ya');
_initialize(2, 7, $3086, 'yu');
_initialize(4, 7, $3088, 'yo'); // -- ra
For l_row:= 0 To 4 Do
_initialize(l_row, 8, $3089+ l_row, 'r'+ k_vowel[l_row]);
// -- wa
_initialize(0, 9, $308F, 'wa');
_initialize(4, 9, $3092, 'wo'); // -- n
_initialize(4, 10, $3093, 'nn');
l_c_check_unique_name_list.Free;
End; // _initialize_code_units |
This will be presented in our paintbox like this:
Note that we used the "courrier new" true text font, but other more nice fonts could be used.
3 - The Hiragana Quiz
3.1 - The Objective We basically want to learn the romaji <-> hiragana mapping : - for any romaji, like "ka", find the hiragana character
- for any hiragana character, find the romaji pronounciation
- for any sound, find the hiragana character
3.2 - The Quiz layout Our basic organization, for the hiragana -> romaji or romaji -> hiragana quiz is : - randomly present the character
- present an array of 3 x 3 potential solution
- the user clicks on a one of the 9 possibilities
- if the selection is correct, present the character with a mnemonic
- if the selection is wrong, present the character corresponding to this selection
Here is a romaji -> hiragana example - the quiz selects a random character, say "ko", and presents 9 hiragana
characters (including "ko") :
- suppose that the user wrongly selects the bottom right "ke". The quiz
displays the romaji corresponding to this letter:
- suppose that the user correctly selects the top right "ko". The quiz
confirms the success and optionally displays some mnemonic (COin) :
The same structure is used for the hiragana -> romaji mapping :
3.3 - The learning range The 9 random potential solutions are selected in a range of the full hiragana character table.
Usually people learn the alphabet progressively - either by column: the vowels, the "K" column, the "S" column etc
- or by row: the "a" row ("a", "ka", "sa" ...) the "i" row ("i", "ki" etc)
Therefore the 9 random letters must be selected among the letters memorized so far. To allow this subrange selection, we simply use a "lasso" on the full hiragana map: the user selects by dragging the mouse a rectangular area of the hiragana
table. The target letter and the potential solution are then drawn from this map. In our case, we used the following subrange:
Note that any area of the table is possible: a full column, several columns, any row, the full table. Even a single letter, which is of little interest of course.
3.4 - Playing the Hiragana sound
We started with the Delphi Multi Media Player, but this was cumbersome since it required some Window. Therefore we selected the direct method Windows MCI primitive:
Function f_play_wave(p_full_file_name: String; p_notify_handle: tHandle): Boolean;
Function _f_send_mci_command(p_mci_command: string; p_handle: tHandle): Boolean;
Var l_result: Integer;
l_error_message_buffer: Array[0..254] Of char;
Begin display(p_mci_command);
l_result:= mciSendString(PChar(p_mci_command), Nil, 0, p_handle);
Result:= l_result= 0;
If Not Result
Then Begin
{get message for returned value}
mciGetErrorString(l_result, l_error_message_buffer, 255);
display(Format('*** %d %s', [l_result, StrPas(l_error_message_buffer)]));
Result:= False;
End; End; // _send_mci_command
Var l_c_file_stream: tFileStream;
Begin // f_play_wave
Result:= FileExists(p_full_file_name);
If Not Result
Then Exit;
l_c_file_stream:= TFileStream.create(p_full_file_name, fmOpenRead);
Try Finally
l_c_file_stream.Free; End;
display_line;
display('> f_play_wave '+ IntToStr(p_notify_handle));
(*$B+*) Result:=
_f_send_mci_command('open '+ p_full_file_name+ ' alias MyFile', 0)
Or
_f_send_mci_command('play MyFile wait', 0)
Or
_f_send_mci_command('close MyFile', p_notify_handle);
(*$B-*) display('< f_play_wave');
End; // f_play_wave |
3.5 - The mnemonics We also use mnemonics for each character. Those are picture whose
pronounciation starts like the sound of the hiragana, and whose picture somehow contains the outline of the character. We already saw the COin. To be able to learn each column, we added a mnemonic display, which displays
the mnemonic for any column of the canonical table. Here is an example when you click anywhere in the "K" column:
3.6 - General outlay Here is a global view of the application
4 - The Hiragana Quiz Code
4.1 - The Hiragana Quiz UML Class diagram Here is the class diagram of the Hiragana Quiz:
4.2 - The architecture For the code, we had to
- display the character at some (x, y) position of a tPaintBox, as well for the canonical column table, as for the question, the 3* 3 quiz and the mnemonic recap (a paintbox with one of the column, displaying the hiragana
and the associated mnemonic)
- define a learning area using the mouse drag
- generate the quiz paintbox containing the potential solutions, generate a random character which is the question
- get the user mouse selection of one of the quiz cell, and update the answer accordingly
We used the following Classes: - c_hiragana_paintbox which is the basic paintbox with mouse to cell
conversions (compute the row and column in a hiragana array corresponding to some mouse position)
- c_all_hiragana_paintbox simply displays the raw katakana unicode bloc
- c_dynamic_hiragana_paintbox contains the m_hiragana_array, which is a two dimensional dynamic array of t_hiragana_info
- c_column_hiragana_paintbox displays the canonical hiragana table.
This area is used to randomly fill the quiz array with 9 hiraganas
- c_learning_hiragana_paintbox is the ancestor for the different kinds of quizes
- c_mnemonic_hiragana_array displays the mnemonics of one column of
the canonical array
- c_quiz_paintbox is the real workorse: when the learning area is selected, it computes the quiz size, fills the quiz with random values and selects on character as the question. It handles the user answer and displays it
- c_romaji_quiz_paintbox displays a romaji question and expects the selection of the correct hiragana answer
- c_sound_hiragana_quiz_ancestor contains the two hiragana paintboxes for the question and the answer
- c_sound_quiz_paintbox plays a sound question and expects the selection of the correct hiragana answer
- c_hiragana_quiz_paintbox displays a hiragana question and expects the selection of the correct romaji answer
4.2.1 - c_all_hiragana_paintbox This paintbox simply displays the raw katakana unicode bloc. It was used to find the mapping from the unicode Kana bloc to the canonical column presentation
Procedure c_all_hiragana_paintbox.handle_paint(p_c_paintbox: tObject);
Procedure _display_column_title;
Var l_column: Integer;
l_column_number: String; Begin
For l_column:= 0 To 15 Do
Begin
l_column_number:= IntToHex(l_column, 1);
display_hiragana_xy(f_x(l_column), k_fat, Ord(l_column_number[1]));
End; End; // _display_column_title
Procedure _display_hiragana;
Var l_row, l_column: Integer;
l_character_code_unit: Integer; Begin
l_character_code_unit:= $3040;
For l_row:= 0 To m_row_count- 1 Do
For l_column:= 0 To m_column_count- 1 Do
Begin
display_hiragana_xy(f_x(l_column), f_y(1+ l_row), l_character_code_unit);
Inc(l_character_code_unit); End;
End; // _display_hiragana Begin // handle_paint
Inherited handle_paint(p_c_paintbox); _display_column_title;
_display_hiragana; End; // initialize_all_hiragana |
4.2.2 - c_dynamic_hiragana_paintbox c_dynamic_hiragana_paintbox contains the m_hiragana_array, which is a two dimensional dynamic array of t_hiragana_info
Each character is managed by the t_hiragana_info record with - the code unit of the character. For the "ka" character, the code unit is $304B
- the romaji name, here ka. To this name is associated a .WAV file for the
sound (ka.wav) and a picture for the mnemonic (ka.png which is a shopping CArt)
- for the mnemonics, the image is stored as a .PNG file (ka.png) and the text ("CArt") is loaded from a mnemonic.txt file
- there are also fields to gather statistics used to spot the characters which were difficult to learn
This is the code used to display the array :
Procedure c_dynamic_hiragana_paintbox.handle_paint(p_c_paintbox: tObject);
Procedure _display_column_title;
Var l_column: Integer; Begin
For l_column:= 0 To m_column_count- 1 Do
display_hiragana_xy(f_x(l_column), k_fat,
Ord(k_column_letter[1+ l_column]));
End; // _display_column_title Procedure _display_hiragana;
Var l_row, l_column: Integer;
Begin
For l_row:= 0 To m_row_count- 1 Do
For l_column:= 0 To m_column_count- 1 Do
display_hiragana_xy(f_x(l_column), f_y(m_title_row+ l_row),
m_hiragana_array[l_row, l_column].m_code_unit);
End; // _display_hiragana Begin // handle_paint
// -- draws the backgroud and a grid
Inherited handle_paint(p_c_paintbox);
If m_title_row> 0
Then _display_column_title; _display_hiragana;
End; // handle_paint |
c_column_hiragana_paintbox
c_column_hiragana_paintbox displays the canonical hiragana table in the right panel of the Form. This display is used for all the quizes. The user selects a learning area of this canonical display, and this selection triggers the
learning sequence. And - the display of the selected area is quite standard and has been presented many times in this site ( Delphi 3d designer,
Selection Rectangle. In addition we trigger the quiz initialization:
Procedure c_column_hiragana_paintbox.handle_mouse_down(p_c_sender: TObject;
p_mouse_button: TMouseButton; p_shfit_state: TShiftState; p_x, p_y: Integer);
Begin m_start_row:= f_y_to_row(p_y);
m_start_column:= f_x_to_column(p_x);
m_in_mouse_move:= True; End; // handle_mouse_down
Procedure c_column_hiragana_paintbox.handle_mouse_up(p_c_sender: TObject;
p_mouse_button: TMouseButton; p_shfit_state: TShiftState; p_x, p_y: Integer);
Begin If m_in_mouse_move
Then Begin
// -- update the end selection coordinates
m_end_row:= 1+ f_y_to_row(p_y);
m_end_column:= 1+ f_x_to_column(p_x);
m_selected_count:= (m_end_row- m_start_row)* (m_end_column- m_start_column);
// -- trigger the initialization of the quiz paintbox
If (m_selected_count> 0) And Assigned(m_on_notify_selection_changed)
Then m_on_notify_selection_changed;
// -- stop tracking the mouse
m_in_mouse_move:= False; End;
End; // handle_mouse_up | - select_row_column_1_2 is used to select the first column as a default
selection learning area when the quiz paintbox is created
4.2.3 - c_learning_hiragana_paintbox The c_learning_hiragana_paintbox simply contains a reference to the canonical
hiragana display. This link is required to access the selected area in order to fill the quiz with randomly selected items from this area
4.2.4 - c_mnemonic_hiragana_array
c_mnemonic_hiragana_array is used to display the mnemonics of one column (see mnemonic above): - the user clicks on any column of the
- the hiragana and the mnemonic of each hiragana are displayed
This was added to help the learning of each column of characters
4.2.5 - c_quiz_paintbox c_quiz_paintbox is used to manage each of the 3 quizes
- when the learning area is selected, it computes the quiz size. The quiz array can be 1* 1, 1* 2, 1* 3, 2* 3 or 3* 3, depending on the size of the selected area. Here is the sizing code
Procedure c_quiz_paintbox.handle_learning_area_changed;
// -- the user changed the selected area // -- adjust the quiz paintbox
Procedure _compute_quiz_array_size;
// -- compute the quiz array size, depending on the selection count
Var l_quiz_row_count, l_quiz_column_count: Integer;
l_selected_count: Integer; Begin
l_quiz_row_count:= 0; l_quiz_column_count:= 0;
With m_c_column_hiragana_paintbox_ref Do
Begin If m_empty_count> 0
Then Begin
// -- adjust the selection count for the sizing,
// -- keep the original for any other purpose
l_selected_count:= m_selected_count- m_empty_count;
End
Else l_selected_count:= m_selected_count;
If (l_selected_count= 0) Or (m_selected_count= 0)
Then force_first_column_selection
Else
If l_selected_count< 3
Then Begin
l_quiz_row_count:= Min(l_selected_count, 3);
l_quiz_column_count:= 1;
// -- initialize the quiz
Self.initialize_empty_hiragana_array;
End
Else Begin
l_quiz_row_count:= 3 ;
If l_selected_count< 6
Then l_quiz_column_count:= 1
Else
If l_selected_count< 9
Then l_quiz_column_count:= 2
Else l_quiz_column_count:= 3;
// -- initialize the quiz
Self.initialize_empty_hiragana_array;
End;
End; // with m_c_column_hiragana_paintbox_ref
m_row_count:= l_quiz_row_count;
m_column_count:= l_quiz_column_count;
// -- this allocates the quiz array
SetLength(m_hiragana_array, m_row_count, m_column_count);
End; // _compute_quiz_array_size
Begin // handle_learning_area_changed // -- recompute the column count
_compute_quiz_array_size; // -- recompute the quiz size
Height:= k_fat+ m_row_count* (m_character_height+ 2* k_fat);
Width:= k_fat+ m_column_count* (m_character_width+ 2* k_fat)+ k_fat;
display_line; fill_quiz_and_choose_random;
End; // handle_learning_area_changed | - after the creation of a new quiz, or after a successful answer, the quiz is
filled with random characters from the learning area :
Procedure c_quiz_paintbox.handle_learning_area_changed;
// -- the user changed the selected area // -- adjust the quiz paintbox
Procedure _compute_quiz_array_size;
// -- compute the quiz array size, depending on the selection count
Var l_quiz_row_count, l_quiz_column_count: Integer;
l_selected_count: Integer; Begin
l_quiz_row_count:= 0; l_quiz_column_count:= 0;
With m_c_column_hiragana_paintbox_ref Do
Begin If m_empty_count> 0
Then Begin
// -- adjust the selection count for the sizing,
// -- keep the original for any other purpose
l_selected_count:= m_selected_count- m_empty_count;
End
Else l_selected_count:= m_selected_count;
If (l_selected_count= 0) Or (m_selected_count= 0)
Then force_first_column_selection
Else
If l_selected_count< 3
Then Begin
l_quiz_row_count:= Min(l_selected_count, 3);
l_quiz_column_count:= 1;
// -- initialize the quiz
Self.initialize_empty_hiragana_array;
End
Else Begin
l_quiz_row_count:= 3 ;
If l_selected_count< 6
Then l_quiz_column_count:= 1
Else
If l_selected_count< 9
Then l_quiz_column_count:= 2
Else l_quiz_column_count:= 3;
// -- initialize the quiz
Self.initialize_empty_hiragana_array;
End;
End; // with m_c_column_hiragana_paintbox_ref
m_row_count:= l_quiz_row_count;
m_column_count:= l_quiz_column_count;
// -- this allocates the quiz array
SetLength(m_hiragana_array, m_row_count, m_column_count);
End; // _compute_quiz_array_size
Begin // handle_learning_area_changed // -- recompute the column count
_compute_quiz_array_size; // -- recompute the quiz size
Height:= k_fat+ m_row_count* (m_character_height+ 2* k_fat);
Width:= k_fat+ m_column_count* (m_character_width+ 2* k_fat)+ k_fat;
display_line; fill_quiz_and_choose_random;
End; // handle_learning_area_changed
Procedure c_quiz_paintbox.fill_quiz_and_choose_random;
Procedure _fill_quiz;
Var // -- do not allow duplicate item in the quiz
l_unique_quiz_romanjj_list: tStringList;
// -- the position of the next item in the quiz
l_quiz_row, l_quiz_column: Integer;
// -- the position of the item in the learning area
l_learning_row, l_learning_column: Integer;
l_candidate_hiragana_info: t_hiragana_info; Begin
// -- debug : fill the array with errors
For l_quiz_row:= 0 To m_row_count- 1 Do
For l_quiz_column:= 0 To m_column_count- 1 Do
m_hiragana_array[l_quiz_row, l_quiz_column].m_code_unit:= k_error_code_unit;
l_unique_quiz_romanjj_list:= tStringList.Create;
// -- fill all the cells of the quiz
For l_quiz_row:= 0 To m_row_count- 1 Do
For l_quiz_column:= 0 To m_column_count- 1 Do
Begin
// -- in the selected area, find an item which is not null
// -- and has not been already selected
Repeat
// -- try a random value
With m_c_column_hiragana_paintbox_ref Do
Begin list_learning_area;
l_learning_row:= m_start_row+ Random(m_end_row- m_start_row)- 1;
l_learning_column:= m_start_column+ Random(m_end_column- m_start_column);
// -- accept if not null and not already in the quiz
If m_c_column_hiragana_paintbox_ref.f_selected_out_of_range(1+ l_learning_row, l_learning_column)
Then display_bug_stop(Format(' quiz_ %2d %2d',
[l_learning_row, l_learning_column]));
l_candidate_hiragana_info:= m_hiragana_array[l_learning_row, l_learning_column];
End; // with m_c_column_hiragana_paintbox_ref
With l_candidate_hiragana_info Do
If (m_code_unit<> 0) And (l_unique_quiz_romanjj_list.IndexOf(m_name)< 0)
Then Begin
l_unique_quiz_romanjj_list.Add(m_name);
m_hiragana_array[l_quiz_row, l_quiz_column]:= l_candidate_hiragana_info;
Break;
End;
Until False;
End; // for l_row, for l_column
l_unique_quiz_romanjj_list.Free; End; // _fill_quiz
Procedure _choose_random;
Var l_random_row, l_random_column: Integer;
Begin Repeat
l_random_row:= Random(m_row_count);
l_random_column:= Random(m_column_count);
m_random_question_info:= m_hiragana_array[l_random_row, l_random_column];
// -- quit either if only 1 cell or if different from previous learning sequence
If (m_row_count* m_column_count= 1)
Or
(m_random_question_info.m_name<> m_previous_random_question_info.m_name)
Then Break;
Until False; // -- display emply item
If Assigned(m_on_notify_mouse_down)
Then
With m_c_column_hiragana_paintbox_ref Do
m_on_notify_mouse_down(m_empty_hiragana_info);
m_success_count:= 0;
m_previous_random_question_info:= m_random_question_info; draw_question;
End; // _choose_random Begin // fill_quiz_and_choose_random
_fill_quiz; _choose_random;
End; // fill_quiz_and_choose_random | Special care is taken to skip empty cells in the canonical array (the "Y",
"W" and "N") columns. In addition, when the quiz has been filled with random values, one of them is selected as the question. The question must be different from the previous one.
And when the question has been chosen, the draw_question method is called to refresh the question display - when the user click on on of the quiz cell:
- either the answer is wrong, and the wrong answer is displayed in red
- or the answer is correct and the correct answer is displayed in green. By default, the user has to click a second time to erase this answer and start a new question, but there is a m_direct_answer boolean which
avoids this second click (when the answer is correct, the next question is directly presented)
In both cases, the statistics of this letter are updated.
4.2.6 - c_romaji_quiz_paintbox
The c_romaji_quiz_paintbox displays a romaji question and expects the selection of the correct hiragana answer
The c_romaji_quiz_paintbox contains - the quiz paintbox (from its c_dynamic_hiragana_paintbox)
- the m_c_question_edit and m_c_answer_edit tEdits
- the overridden to display the question, display the answer, clear the answer
4.2.7 - c_sound_hiragana_quiz_ancestor This class contains the two hiragana paintboxes for the question and the answer
4.2.8 - c_sound_quiz_paintbox c_sound_quiz_paintbox plays a sound and expects the user to select the correct hiragana. In the following figure, the sound "ee" was played and the user correctly selected the "e" hiragana :
and - the draw_question only plays the sound
4.2.9 - c_hiragana_quiz_paintbox
c_hiragana_quiz_paintbox presents a hiragana character, and expects the selection of the correct romaji:
Here the paint method of the quiz has to be overridden to display the romaji instead of the default hiragana
5 - Mini How To 5.1 - General layout Here is the general layout :
5.2 - The Mnemonic recap To display the mnemonics of one column |
start the exe | | select the "mnemonic" RadioButton | |
the mnemonics of the first column are displayed | To display another column | in the canonical display, click on any column |
| the mnemonics of this column are displayed | Here is the display of the "K" column:
5.3 - The 3 quizes To use a quiz | start the exe |
| select one of the 3 quiz type by clicking one of the radiobuttons : romaji_quiz_, hiragana_quiz_ or sound_quiz_ |
| then - the canonical hiragana array is displayed on the right, and by default the 5 characters of the first column are selected as the learning area
- the quiz array with its question question and answer displays are presented. The quiz array contains 3 choices, since the learning area only contains 5 characters
|
| you can start answering the question. And - clicking on the wrong cell displays the corresponding answer in red.
- clicking on the right cell confirms the answer in green. Clicking a second time on this cell presents the next text
|
To change the learning area
| click and drag the mouse over the canonical area | |
the quiz array is presented, and, depending on the learning area size, this array can contain be 1, 2, 3, 6 or 9 cells | The rest of the quiz is handled as before
Here is an example of selecting some learning area of the hiragana to romaji quiz. The question is "hi"
This is the display if the user selects some wrong answer "mi" : and here is the correct answer "hi":
To change the quiz type, click on another radiobutton
The learning sequence is - 1 - select a quiz
- 2 - select a learning area
- 3 - a question is presented
- 4 - select an answer in the quiz
If the selection is wrong, the wrong character will be presented in red If the selection is right, the correct character will be presented in green,
and clicking a second time on the same area presents a new question - 5 - the quiz displays the right or wrong answer, along with the mnemonic
and - for a given learning area, you can run as many questions as you want
- for a given quiz, you can select any learning area
- you can change the quiz
6 - Some Remarks 6.1 - About the code The Class structure seems overly complex for this quiz family. The only
alternate solution would have been to add a "question / answer" display Class, with 2 descendents: text or hiragana. This hardly simplifies the structure. Using a Delphi Unicode version could have simplified the display of all the
Kana unicodes. We could have used - a tStringGrid for the canonical display and the quiz
- tEdits for all the questions and answers.
At some places the use of events could have been replaced by Abstract methods
in the ancestor.
6.2 - The learning efficiency This part is obviously very personal. To recap the learning experience: - I started with the excellent Learn Japanese Language Free and Easy site.
The first Hiragana lesson teach you each column in turn. To memorize the first column, I looked at the mnemonics.
For the second column, I felt necessary to write every letter on a piece of paper. For the third, I started to forget the previous columns. - I then created the quiz, to check that all previously learned character were still remembered
- I then added the hiragana to romaji quiz and the sound to hiragana quiz
So it took me around a week or so to learn the hiragana glyphs. On internet, some people (or school advertising) brag about a 1 day effort. I am not sure I
could accomplish this. My lessons were around 1 hour for learning and writing a new column. Then I checked the previously learned letters with the quiz. But then there were some mental rehearsal during the day (walking, or musing at a
bus stop). So if I learned in around 10 days, it took more then 10 hours : the mental training, the time to let it "sink in", the sleep rehearsal (awaking during the night thinking about those glyphs / sounds / mnemonics). Certainly
the japanese adult schools with new students every week must have a better idea about the average performance, the difficulties, the best technique to use.
Learning the Hiragana alphabet is only the very first step. So I started to
learn some vocabulary (train, subway, water...), and some "survival" phrases (where is the nearest subway station ?). For the vocabulary, the Learn Japanese
Language Free and Easy site is again very helpful. For the phrases, I found the Japanese Phrases for Travellers site very complete. It has over 1.200 phrases.
Both the vocabulary and the sentence were incorporated in 2 other Delphi applications to avoid the download times. This might be published later.
So I now feel reasonably ready to fly to Japan, just at Hanami time (cherry
blossom). Should you recognize my French béret (a hat like the Argentinian Gauchos) near Takayama or Miyajima, do not hesitate to meet me ! I will be only too
happy to try my very very basic Japanese. Hai !.
7 - Download the Sources This is the first time you can download the .EXE from this site. Some people
might want to use the quiz directly without going through a Delphi compilation. Here are the source code files: You can also download the .EXE (this is a first for this site):
The .ZIP file(s) contain: - the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
- any .TXT for parameters, samples, test data
- all units (.PAS) for units
Those .ZIP
- are self-contained: you will not need any other product (unless expressly mentioned).
- for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
- will not modify your PC in any way beyond the path where you placed the .ZIP (no registry changes, no path creation etc).
To use the .ZIP: - create or select any folder of your choice
- unzip the downloaded file
- using Delphi, compile and execute
To remove the .ZIP simply delete the folder. The Pascal code uses the Alsacian notation, which prefixes identifier by
program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lass etc. This notation is presented in the Alsacian Notation paper. The .ZIP file(s) contain:
- the main program (.DPROJ, .DPR, .RES), the main form (.PAS, .ASPX), and any other auxiliary form or files
- any .TXT for parameters, samples, test data
- all units (.PAS .ASPX and other) for units
Those .ZIP
- are self-contained: you will not need any other product (unless expressly mentioned).
- will not modify your PC in any way beyond the path where you placed the .ZIP
(no registry changes, no path outside from the container path creation etc).
To use the .ZIP: - create or select any folder of your choice.
- unzip the downloaded file
- using Delphi, compile and execute
To remove the .ZIP simply delete the folder. The Pascal code uses the Alsacian notation, which prefixes identifier by program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre,
F_unction, C_lass etc. This notation is presented in the Alsacian Notation paper.
As usual:
- please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken links or had some problem downloading the file. Resulting corrections will
be helpful for other readers
- we welcome any comment, criticism, enhancement, other sources or reference suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
- or more simply, enter your (anonymous or with your e-mail if you want an answer) comments below and clic the "send" button
- and if you liked this article, talk about this site to your fellow developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate:
the more traffic and Google references we get, the more articles we will write.
8 - Links Here are a couple of links - Learn
Japanese Language Free and Easy the best site I found so far, complete whith Hiragana, Katakana, vocabulary, grammar etc. I only scratched the surface of its treasures
- Japanese Phrases for Travellers has over 1.200 "survival phrases", phrases, organized by topic (restaurant, travel etc). The main advantages were
- its completeness
- the relevance of the phrases for a traveller
- the presence of sound (mp3)
- the presentation of Hiragana and Kanji (where relevant) for each phrase
- I also subscribed to JapanesePod101 site. I had a hard time to understand if it really was free, as claimed. It turned out that I had to
pay a $1 charge for "bandwidth". So I accepted. I did not use the site very much, since I quickly understood that listening to videos was not enough: some pen and pencil rehearsals were still required. So I wrote the quiz.
Then, while I still was battling with my Delphi quiz, I was charged $25 "premium" whatever. I cancelled everything and asked for a refund. The send me a mail that the refund was performed, and I still have to check. I
dislike those "free" with all kind of formulas where I did not see the additional charges. Anyway, some reviews were reasonably favorable for a "quick learning" of Japanese.
- I also bought a couple of books
- a couple of books about sightseeing
- then "Japanese 1 from zero" by George trombley Jr and Yukari Katakana. I just read up to page 70 so far, but this presented me the "wa", "ka", "kore" constructs, which were about enough to start understand
and rehearse the "survival phrases"
On the coding side, for Unicode we published - Delphi Unicode Migration which
presents the unicode system (code points, code units, Basic Multilingual Plane, Utf8, Utf16, surrogates and composites, and the points to watch for when migrating to a post Delphi 2009 version)
9 - The author
Felix John COLIBRI works at the Pascal Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly
active in the area of custom software development (new projects, maintenance, audits, BDE migration, Delphi
Xe_n migrations, refactoring), Delphi Consulting and Delph
training. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, FireBird, Tcp/IP, Web Services, OOP / UML, Design Patterns, Unit Testing training sessions. |